package com.wn.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.Serializable;
import java.util.Calendar;
import java.util.Date;

/**
 * 用于生成Token和解析Token
 *
 */
public class JwtUtils {

    private static final String SECRET="Ziroom";
    private static  final String EXP = "exp";
    private static final String PAYLOAD = "payload";

    /**
     * 生成token
     * @param obj    要生成token的对象
     * @param maxAge token的过期时间   单位秒数
     * @return       token的字符串！
     * @param <T>  声明当前方法是泛型方法。泛型类的泛型可以被擦除且不够灵活（泛型类的泛型是构建类对象时完成赋值的。而泛型方法是调用方法时赋值的）
     */
    public static <T>String createToken(T obj, int maxAge){
        try{
            Calendar instance=Calendar.getInstance();
            //-- 设置单位为秒数
            instance.add(Calendar.MILLISECOND,maxAge);

            //创建jwt builder  建造者模式！
            JWTCreator.Builder builder = JWT.create();

            //-- ObjectMapper是Jackson的基础类
            ObjectMapper mapper = new ObjectMapper();
            //--  把JavaBean序列化成JSON字符串！
            String jsonStr = mapper.writeValueAsString(obj);

            //Use JWT.create(), configure the claims, and then call sign(algorithm) to sign the JWT.
            String token = builder
                    .withClaim(PAYLOAD,jsonStr)
                    .withClaim(EXP,instance.getTimeInMillis())
                    //.withExpiresAt(instance.getTime())//指定令牌的过期时间
                    .sign(Algorithm.HMAC256(SECRET));//签发算法

            return token;
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    /***
     * 验证token
     */
    public static <T>T verifyToken(String token,Class<T> clz){
        try{
            JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
            DecodedJWT verify = jwtVerifier.verify(token);

            //-- 获取过期时间
            Long dateMillions = verify.getClaim(EXP).asLong();
            Date date = new Date(dateMillions);
            //-- 过期时间在当前时间之前，表示已经过期了
            if (date.before(new Date())){
                return null;
            }
            //-- 如果没有过期则验证Token 的信息
            String jsonStr = verify.getClaim(PAYLOAD).asString();

            ObjectMapper objectMapper = new ObjectMapper();
            T t = objectMapper.readValue(jsonStr, clz);
            return t;
        } catch (JsonMappingException e) {
            throw new RuntimeException(e);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static <T>T verifyTokenWithoutVerifyExp(String token,Class<T> clz){
        try{
            JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
            //-- token如果过期了 这里会抛出异常的！
            DecodedJWT verify = jwtVerifier.verify(token);

            //-- 如果没有过期则验证Token 的信息
            String jsonStr = verify.getClaim(PAYLOAD).asString();

            ObjectMapper objectMapper = new ObjectMapper();
            T t = objectMapper.readValue(jsonStr, clz);
            return t;
        } catch (JsonMappingException e) {
            throw new RuntimeException(e);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {

        User user = new User(1,"张三",19,"男");

        String token = createToken(user,30);
        //String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJwYXlsb2FkIjoie1wiaWRcIjoxLFwibmFtZVwiOlwi5byg5LiJXCIsXCJhZ2VcIjoxOSxcImdlbmRlclwiOlwi55S3XCJ9IiwiZXhwIjoxNjc1OTEyMTg0NzYyfQ._CuviQJW4uQlU9WMIzBfgY94FxzVr3i2EdvXc0Mr-ak";
        System.out.println(token);

        User u = verifyToken(token, User.class);
        System.out.println(u);

    }

}

class User implements Serializable {
    public  static final long serialVersionUID = 42L;
    private int id;
    private String name;
    private int age;
    private String gender;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                '}';
    }

    public User(int id, String name, int age, String gender) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    public User() {
    }
}